﻿
using System.Data;
using System.Linq;
using System.Collections.Generic;

using Gola.VN.Scb.Web.Dal;
using Gola.VN.Scb.Web.Common;
using Gola.VN.Scb.Web.Common.Models;
using Gola.VN.Scb.Web.Common.Entities;

namespace Gola.VN.Scb.Web.Bll
{
    internal class CategoryBiz : BaseBiz
    {
        internal CategoryBiz(MainDao mainDao)
            : base(mainDao)
        {
        }

        #region initialize data
        internal void InitializeData()
        {
            // 1. get categories as tree from database
            List<CategoryEntity> categoriesAsTree = InitializeCategoriesAsTree();

            // 2. update categories as tree into memory
            InitializeCategoriesAsTree(categoriesAsTree);

            // 3. get category as flat from database
            IEnumerable<CategoryEntity> categoriesAsFlat = InitializeCategoriesAsFlat();

            // 4. update category as flat into memory
            InitializeCategoriesAsFlat(categoriesAsFlat);
        }

        private List<CategoryEntity> InitializeCategoriesAsTree()
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Sql;
            package.Event = DaoEvent.GetCategoriesAsTree;
            _mainDao.Execute(package);

            DataTable datas = package.Output as DataTable;
            List<CategoryEntity> categories = new List<CategoryEntity>();

            CategoryEntity parent = null;
            CategoryEntity sub = null;

            foreach (DataRow row in datas.Rows)
            {
                int id = int.Parse(row[QueryConstants.Category.Id].ToString());
                int parentId = int.Parse(row[QueryConstants.Category.ParentId].ToString());
                string name = row[QueryConstants.Category.Name].ToString();
                string notes = row[QueryConstants.Category.Notes].ToString();

                #region add level 1
                if (parentId == 0)
                {
                    parent = new CategoryEntity();
                    parent.Id = id;
                    parent.ParentId = parentId;
                    parent.Name = name;
                    parent.Note = notes;
                    categories.Add(parent);

                    continue;
                }
                #endregion

                #region add level 2
                if (parentId == parent.Id)
                {
                    sub = new CategoryEntity();
                    sub.Id = id;
                    sub.ParentId = parentId;
                    sub.Name = name;
                    sub.Note = notes;
                    parent.SubCategories.Add(sub);

                    continue;
                }
                #endregion

                #region add level 3
                if (parentId == sub.Id)
                {
                    CategoryEntity subsub = new CategoryEntity();
                    subsub.Id = id;
                    subsub.ParentId = parentId;
                    subsub.Name = name;
                    subsub.Note = notes;
                    sub.SubCategories.Add(subsub);

                    continue;
                }
                #endregion
            }

            return categories;
        }

        private void InitializeCategoriesAsTree(List<CategoryEntity> categoriesAsTree)
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.SetCategoriesAsTree;
            package.Input = categoriesAsTree;

            _mainDao.Execute(package);
        }

        private IEnumerable<CategoryEntity> InitializeCategoriesAsFlat()
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Sql;
            package.Event = DaoEvent.GetCategoriesAsFlat;

            _mainDao.Execute(package);

            return package.Output as IEnumerable<CategoryEntity>;
        }

        private void InitializeCategoriesAsFlat(IEnumerable<CategoryEntity> categoriesAsFlat)
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.SetCategoriesAsFlat;
            package.Input = categoriesAsFlat;

            _mainDao.Execute(package);
        }
        #endregion

        internal List<UICategoryModels> GetSubCategories(int id)
        {
            IEnumerable<CategoryEntity> categories = GetCategoriesAsFlat();
            //get top product for each subcategories.
            ProductBiz biz = new ProductBiz(_mainDao);

            return categories.
                Where(q => q.ParentId == id).Select(q => new UICategoryModels//level 1
                {
                    Id = q.Id,
                    Name = q.Name,
                    TopProducts = biz.GetProductsByCategoryId(q.Id).Take(10).ToList(),
                    Subcategories =//level 2
                        (categories.Where(
                            p => p.ParentId == q.Id).Select(
                                p => new UICategoryModels
                                {
                                    Id = p.Id,
                                    Name = p.Name//,
                                    //Subcategories = (categories.Where(
                                    //    a => a.ParentId == p.Id)).Select(b => new UICategoryModels
                                    //    {
                                    //        Id = b.Id,
                                    //        Name = b.Name
                                    //    }).Take(5)
                                })).Take(15)
                }).ToList();
        }

        internal List<UICategoryModels> GetNavigateData(int id)
        {
            var navigateData = new Stack<UICategoryModels>();
            var currentNode = GetCategoryById(id);
            var subCurrentNode = GetSubCategories(id);

            navigateData.Push(currentNode.ToUICategoryModels(subCurrentNode));

            //process parent node.
            var parentNode = GetCategoryById(currentNode.ParentId);
            if (parentNode != null)
            {
                navigateData.Push(parentNode.ToUICategoryModels(GetSubCategories(parentNode.Id)));
                while (parentNode.ParentId > 0)
                {
                    parentNode = GetCategoryById(parentNode.ParentId);
                    navigateData.Push(parentNode.ToUICategoryModels(GetSubCategories(parentNode.Id)));
                }
            }

            return navigateData.ToList();
        }

        internal CategoryEntity GetCategoryById(int id)
        {
            if (id == 0)
                return null;

            IEnumerable<CategoryEntity> categories = GetCategoriesAsFlat();
            return categories.FirstOrDefault(q => q.Id == id);
        }

        internal List<UICategoryModels> GetChildCategories(int id)
        {
            if (id == 0)
                return null;

            IEnumerable<CategoryEntity> categories = GetCategoriesAsFlat();
            return categories.Where(q => q.ParentId == id).Select(
                           p => new UICategoryModels
                           {
                               Id = p.Id,
                               Name = p.Name
                           }).ToList();
        }

        internal IEnumerable<CategoryEntity> GetCategoriesAsFlat()
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.GetCategoriesAsFlat;
            _mainDao.Execute(package);

            return package.Output as IEnumerable<CategoryEntity>;
        }

        internal List<CategoryEntity> GetCategoriesByProviderId(int providerId)
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.GetProducts;
            _mainDao.Execute(package);

            IEnumerable<ProductEntity> products = package.Output as IEnumerable<ProductEntity>;
            IEnumerable<CategoryEntity> categories = GetCategoriesAsFlat();

            var selectedIds = (from p in products
                               where p.ProviderId == providerId
                               select p.CategoryId).Distinct();

            return (from c in categories where selectedIds.Contains(c.Id) select c).ToList();
        }

        internal List<CategoryEntity> GetCategoriesAsTree()
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.GetCategoriesAsTree;
            _mainDao.Execute(package);

            return package.Output as List<CategoryEntity>;
        }

        internal List<CategoryEntity> GetSubCategoriesAsTree(int id)
        {
            List<CategoryEntity> categoriesAsTree = GetCategoriesAsTree();
            CategoryEntity category = categoriesAsTree.FirstOrDefault(c => c.Id == id);
            return (category != null) ? category.SubCategories : new List<CategoryEntity>();
        }

        internal List<UICategoryModels> GetCloudCategory(int id = 0)
        {
            var category = GetCategoryById(id);
            var categories = GetCategoriesAsFlat();

            if (category == null)
                category = categories.First(q => q.ParentId == id);

            return categories.Where(q => ((q.ParentId == category.ParentId) || (q.ParentId == category.Id))).Select(p => new UICategoryModels
            {
                Id = p.Id,
                Name = p.Name,
                Subcategories = GetChildCategories(p.Id)
            }).ToList();
        }
    }
}
